package com.huilian.iotbox.mqtt.server.base.service.impl;

import com.huilian.iotbox.aliyun.service.PubService;
import com.huilian.iotbox.data.cmd.PositiveDataFrameCMD;
import com.huilian.iotbox.data.cmd.VendmachPositiveDataFrameCMD;
import com.huilian.iotbox.data.common.RedisKey;
import com.huilian.iotbox.data.constants.DelayConstants;
import com.huilian.iotbox.data.dto.*;
import com.huilian.iotbox.data.enums.CommodityStockRecordFirstTypeEnum;
import com.huilian.iotbox.data.enums.CommodityStockRecordSecondTypeEnum;
import com.huilian.iotbox.data.dto.DisplayRackItemDto;
import com.huilian.iotbox.data.enums.ShipmentStatusEnum;
import com.huilian.iotbox.data.po.*;
import com.huilian.iotbox.data.service.RefundService;
import com.huilian.iotbox.data.service.SystemConfigService;
import com.huilian.iotbox.data.utils.DateTimeUtils;
import com.huilian.iotbox.data.utils.IotBoxUtil;
import com.huilian.iotbox.data.vo.DeviceVo;
import com.huilian.iotbox.data.vo.OrderItemVo;
import com.huilian.iotbox.data.vo.OrderVo;
import com.huilian.iotbox.mqtt.server.base.dao.OrderDao;
import com.huilian.iotbox.mqtt.server.base.producer.RabbitProducer;
import com.huilian.iotbox.mqtt.server.base.service.*;
import com.huilian.tool.weixin.service.WXSendMessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author lizhiquan
 * @CreateDate: 2021-04-02-15-33
 * @Description:
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderDao orderDao;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private MerchantStoreService merchantStoreService;
    @Autowired
    private RefundService refundService;
    @Autowired
    private DisplayRackItemService displayRackItemService;
    @Autowired
    private PubService pubService;
    @Resource
    private CommodityStockRecordService commodityStockRecordService;
    @Resource
    private CommodityStockRecordItemService commodityStockRecordItemService;
    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private RabbitProducer rabbitProducer;
    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;
    @Autowired
    private WXSendMessageService wxSendMessageService;
    @Override
    public Integer updateStatusById(OrderDto order) {
        return orderDao.updateStatusById(order);
    }

    @Override
    public OrderDto getById(Long orderId) {
        return orderDao.getById(orderId);
    }

    @Override
    public Integer insert(OrderDto order) {
        return orderDao.insert(order);
    }


    @Override
    public Order findOneOrderByCondition(Order condition) {
        if (StringUtils.isEmpty(condition)) {
            return null;
        }
        return orderDao.findOneOrderByCondition(condition);
    }

    @Override
    public Boolean processingShipments(String content, DeviceVo deviceVo, String payNo, String productKey) {
        Integer totalNum = IotBoxUtil.singleHex2Int(content.substring(40, 42));
        String data = content.substring(42, content.length() - 4);

        Integer beginIndex = 0;
        Integer endIndex = 2;
        // 失败数量
        Integer failNum = 0;
        // 总的出货数量，用来判断是否自动退款
        Integer shipmentTotalNum = 0;
        Long orderId = 0L;
        List<OrderItem> shipmentList = new ArrayList<>();
        // 出货
        long l_1 = System.currentTimeMillis();
//        List<OrderItemVo> orderItemList = orderItemService.findOrderItemList(payNo, deviceVo.getDeviceNo());
        log.info("售货机是否收到订阅消息，处理慢检查点_1：" + (System.currentTimeMillis() - l_1));
        boolean whetherToInsertInventoryRecords = false;
//        if (orderItemList.get(0).getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode()) || orderItemList.get(0).getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())) {
//            for (OrderItemVo orderItem : totalNum) {
        for (int i = 0; i < totalNum; i++) {
            OrderItemDto orderItemDto = new OrderItemDto();
            // 逻辑是这样的，如果有开启重新出货，修改失败次数和订单状态，然后等重新出货后重新判断，如果还是失败了就开始判断是否自动退款
            final Integer line = IotBoxUtil.singleHex2Int(data.substring(beginIndex, endIndex));
            final Integer rank = IotBoxUtil.singleHex2Int(data.substring(beginIndex + 2, endIndex + 2));
            final Integer num = IotBoxUtil.singleHex2Int(IotBoxUtil.reverseHex(data.substring(beginIndex + 4, endIndex + 6)));
            orderItemDto.setHierarchy(line);
            orderItemDto.setFreightLane(rank);
            orderItemDto.setPayNo(payNo);
            OrderItemVo orderItemVo = orderItemService.getOrderItem(orderItemDto);
            orderId = orderItemVo.getOrderId();
            if (orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode()) || orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())) {
                Integer shipmentNum = orderItemVo.getNum();
                shipmentTotalNum += orderItemVo.getNum();
                if (orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())) {
                    // 如果进来就说明已经重新出货过一次了
                    shipmentNum = orderItemVo.getShipmentFailureNum();
                } else if (!orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode())) {
                    continue;
                }
                if (num <= 0) {
                    orderItemVo.setShipmentStatus(deviceVo.getRetry() && !orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())
                            ? ShipmentStatusEnum.RESHIPMENT.getCode() : ShipmentStatusEnum.FAIL.getCode());
                    orderItemVo.setShipmentFailureNum(orderItemVo.getNum());
                    failNum = +shipmentNum;
                } else if (shipmentNum >= num) {
                    orderItemVo.setShipmentStatus(ShipmentStatusEnum.SUCCESS.getCode());
                } else {
                    orderItemVo.setShipmentStatus(deviceVo.getRetry() && !orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())
                            ? ShipmentStatusEnum.RESHIPMENT.getCode() : ShipmentStatusEnum.PART_FAIL.getCode());
                    orderItemVo.setShipmentFailureNum(shipmentNum - num);
                }

                if (orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.PART_FAIL.getCode()) || orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.FAIL.getCode())) {
                    DisplayRackItemDto displayRackItemDto = new DisplayRackItemDto();
                    displayRackItemDto.setDeviceId(deviceVo.getId());
                    displayRackItemDto.setHierarchy(line);
                    displayRackItemDto.setFreightLane(rank);
                    displayRackItemDto.setIsDelete(false);

                    long l_2 = System.currentTimeMillis();
                    displayRackItemService.editVendMachStock(displayRackItemDto, shipmentNum, 0);
                    log.info("售货机是否收到订阅消息，处理慢检查点_2：" + (System.currentTimeMillis() - l_2));
                }

                if (ShipmentStatusEnum.RESHIPMENT.getCode().equals(orderItemVo.getShipmentStatus()) && deviceVo.getRetry()) {
                    OrderItem item = new OrderItem();
                    item.setHierarchy(line);
                    item.setFreightLane(rank);
                    item.setNum(orderItemVo.getShipmentFailureNum());
                    shipmentList.add(item);
                }
                long l_3 = System.currentTimeMillis();
                orderItemService.update(orderItemVo);
                log.info("售货机是否收到订阅消息，处理慢检查点_3：" + (System.currentTimeMillis() - l_3));
                beginIndex = beginIndex + 8;
                endIndex = endIndex + 8;
            }

            String memberPayDetailKey = String.format(RedisKey.LIMITATION_SIMULTANEOUS_PAYMENT_VENDING_MACHINE, deviceVo.getId());
            if (deviceVo.getRetry() && !StringUtils.isEmpty(shipmentList) && shipmentList.size() != 0) {
                log.info("发送mqtt重新出货");
                String random = RandomStringUtils.random(6, true, true);
                String command = PositiveDataFrameCMD.penetrate(VendmachPositiveDataFrameCMD.shipment(deviceVo.getDeviceNo(), random, shipmentList));
                pubService.pub(command, deviceVo.getDeviceNo(), productKey);
                try {
                    Order order = new Order();
                    order.setId(orderId);
                    order.setDeviceNo(deviceVo.getDeviceNo());
                    order.setPayNo(random);
                    order.setShipmentStatus(ShipmentStatusEnum.RESHIPMENT.getCode());
                    long l_4 = System.currentTimeMillis();
                    orderDao.updateShipmentStatusById(order);
                    log.info("售货机是否收到订阅消息，处理慢检查点_4：" + (System.currentTimeMillis() - l_4));
                    final SystemConfig systemConfig = systemConfigService.findOneSystemConfigById(1);
                    if(!StringUtils.isEmpty(deviceVo.getPayLimitTime()) && deviceVo.getPayLimitTime()>0){
                        redisCacheTemplate.opsForValue().set(memberPayDetailKey, 1, deviceVo.getPayLimitTime(), TimeUnit.SECONDS);
                        rabbitProducer.sendVendingMachineReplayLazyQueue(DelayConstants.VENDING_MACHINE, order, 1000 * deviceVo.getPayLimitTime());
                    }else {
                        rabbitProducer.sendVendingMachineReplayLazyQueue(DelayConstants.VENDING_MACHINE, order, 1000 * systemConfig.getOnLineInsertCoinNotNoticeTime());
                    }
                } catch (Exception e) {
                    log.error("重新出货，并且重新发一个延时mq");
                    e.printStackTrace();
                }

            } else {
                Order order = new Order();
                order.setId(orderId);
                if (failNum.equals(shipmentTotalNum)) {
                    log.info("出货失败,orderId:{}", order.getId());
                    order.setShipmentStatus(ShipmentStatusEnum.FAIL.getCode());
                    VendingDeviceFaultRecord(deviceVo);
                } else if (failNum > 0) {
                    log.info("部分失败,orderId:{}", order.getId());
                    order.setShipmentStatus(ShipmentStatusEnum.PART_FAIL.getCode());
                    whetherToInsertInventoryRecords = true;
                    VendingDeviceFaultRecord(deviceVo);
                } else {
                    log.info("出货成功,orderId:{}", order.getId());
                    order.setShipmentStatus(ShipmentStatusEnum.SUCCESS.getCode());
                    whetherToInsertInventoryRecords = true;
                    redisCacheTemplate.delete(memberPayDetailKey);
                }
                long l_5 = System.currentTimeMillis();
                orderDao.updateShipmentStatusById(order);
                log.info("售货机是否收到订阅消息，处理慢检查点_5：" + (System.currentTimeMillis() - l_5));
            }
        }
        //出货成功,部分失败才插入库存记录
        if (whetherToInsertInventoryRecords) {
            Order order = new Order();
            order.setId(orderId);
            addCommodityStockRecord(order);
        }
        return true;
    }

    /**
     * 格子机处理订单
     */
    @Override
    public Boolean LatticeProcessingShipments(DeviceVo deviceVo, String payNo, String productKey, Integer hierarchy, Integer freightLane, Integer shipmentStatus) {
        OrderItemVo orderItem = orderItemService.findOrderItem(payNo, deviceVo.getDeviceNo());
        if (orderItem.getShipmentStatus().equals(ShipmentStatusEnum.WAIT.getCode()) || orderItem.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())) {
            orderItem.setHierarchy(hierarchy);
            orderItem.setFreightLane(freightLane);
            if (shipmentStatus.equals(1)) { // 开启成功
                orderItem.setShipmentStatus(ShipmentStatusEnum.SUCCESS.getCode());
            } else {
                orderItem.setShipmentStatus(deviceVo.getRetry() && !orderItem.getShipmentStatus().equals(ShipmentStatusEnum.RESHIPMENT.getCode())
                        ? ShipmentStatusEnum.RESHIPMENT.getCode() : ShipmentStatusEnum.FAIL.getCode());
            }
            if (orderItem.getShipmentStatus().equals(ShipmentStatusEnum.PART_FAIL.getCode()) || orderItem.getShipmentStatus().equals(ShipmentStatusEnum.FAIL.getCode())) {
                DisplayRackItemDto displayRackItemDto = new DisplayRackItemDto();
                displayRackItemDto.setDeviceId(deviceVo.getId());
                displayRackItemDto.setHierarchy(hierarchy);
                displayRackItemDto.setFreightLane(freightLane);
                displayRackItemDto.setIsDelete(false);
                displayRackItemService.editVendMachStock(displayRackItemDto, 1, 0);
            }

            String memberPayDetailKey = String.format(RedisKey.LIMITATION_SIMULTANEOUS_PAYMENT_VENDING_MACHINE, deviceVo.getId());
            if (ShipmentStatusEnum.RESHIPMENT.getCode().equals(orderItem.getShipmentStatus()) && deviceVo.getRetry()) {
                log.info("发送mqtt重新出货");
                String random = RandomStringUtils.random(6, true, true);
                String command = PositiveDataFrameCMD.penetrate(VendmachPositiveDataFrameCMD.LatticeShipment(random, orderItem.getHierarchy(), orderItem.getFreightLane()));
                pubService.pub(command, deviceVo.getDeviceNo(), productKey);
                orderItemService.update(orderItem);
                // 加一个缓存来判断吧，没必要发太多的mq
                try {
                    String key = String.format(RedisKey.VENDING_MACHINE_RE_STATUS, deviceVo.getDeviceNo(), orderItem.getOutTradeNo());
                    Integer interceptState = (Integer) redisCacheTemplate.opsForValue().get(key);
                    if (interceptState == null) {
                        Order order = new Order();
                        order.setId(orderItem.getOrderId());
                        order.setDeviceNo(deviceVo.getDeviceNo());
                        order.setPayNo(random);
                        order.setShipmentStatus(ShipmentStatusEnum.RESHIPMENT.getCode());
                        final SystemConfig systemConfig = systemConfigService.findOneSystemConfigById(1);
                        if(!StringUtils.isEmpty(deviceVo.getPayLimitTime()) && deviceVo.getPayLimitTime()>0){
                            redisCacheTemplate.opsForValue().set(memberPayDetailKey, 1, deviceVo.getPayLimitTime(), TimeUnit.SECONDS);
                            rabbitProducer.sendVendingMachineReplayLazyQueue(DelayConstants.VENDING_MACHINE, order, 1000 * deviceVo.getPayLimitTime());
                        }else {
                            rabbitProducer.sendVendingMachineReplayLazyQueue(DelayConstants.VENDING_MACHINE, order, 1000 * systemConfig.getOnLineInsertCoinNotNoticeTime());
                        }
                    }
                } catch (Exception e) {
                    log.info("重新出货，并且重新发一个延时mq");
                }
            } else {
                orderItemService.update(orderItem);
                OrderItemVo orderItemVo = orderItemService.findOrderItemCountByOrderId(orderItem.getOrderId(),orderItem.getId());
                if (!StringUtils.isEmpty(orderItemVo)) {
                    String command = PositiveDataFrameCMD.penetrate(VendmachPositiveDataFrameCMD.LatticeShipment(orderItemVo.getPayNo(), orderItemVo.getHierarchy(), orderItemVo.getFreightLane()));
                    pubService.pub(command, deviceVo.getDeviceNo(), productKey);
                    log.info("格子机处理订单,payNo:{},hierarchy:{},freightLane:{},command:{}",orderItemVo.getPayNo(),orderItemVo.getHierarchy(), orderItemVo.getFreightLane(),command);
                } else {
                    Order order = new Order();
                    OrderItemVo item = orderItemService.findSumShipment(orderItem.getOrderId());
                    order.setId(orderItem.getOrderId());
                    if (item.getNum().equals(item.getShipmentFailureNum())) {
                        log.info("出货失败,orderId:{}",order.getId());
                        order.setShipmentStatus(ShipmentStatusEnum.FAIL.getCode());
                        VendingDeviceFaultRecord(deviceVo);
                    } else if (item.getShipmentFailureNum() > 0) {
                        log.info("部分失败,orderId:{}",order.getId());
                        order.setShipmentStatus(ShipmentStatusEnum.PART_FAIL.getCode());
                        addCommodityStockRecord(order);
                        VendingDeviceFaultRecord(deviceVo);
                    } else {
                        log.info("出货成功,orderId:{}",order.getId());
                        order.setShipmentStatus(ShipmentStatusEnum.SUCCESS.getCode());
                        addCommodityStockRecord(order);
                        redisCacheTemplate.delete(memberPayDetailKey);
                    }
                    orderDao.updateShipmentStatusById(order);
                }
            }
        }
        return true;
    }

    @Override
    public OrderVo findByPayNo(String payNo,String deviceNo) {
        return orderDao.findByPayNo(payNo,deviceNo);
    }

    private void addCommodityStockRecord(Order order) {
        OrderDto orderDto = new OrderDto();
        orderDto.setId(order.getId());
        long l_6 = System.currentTimeMillis();
        OrderVo orderListByApplet = orderDao.findOrderListByApplet(orderDto);
        log.info("售货机是否收到订阅消息，处理慢检查点_6：" + (System.currentTimeMillis() - l_6));
        CommodityStockRecordDto addCommodityStockRecordDto = new CommodityStockRecordDto();
        addCommodityStockRecordDto.setUserId(orderListByApplet.getUserId());
        addCommodityStockRecordDto.setMerchantStoreId(orderListByApplet.getMerchantStoreId());
        addCommodityStockRecordDto.setDeviceId(orderListByApplet.getDeviceId());
        addCommodityStockRecordDto.setOutTradeNo(orderListByApplet.getOutTradeNo());
        addCommodityStockRecordDto.setDescription("售货机售卖");
        addCommodityStockRecordDto.setFirstType(CommodityStockRecordFirstTypeEnum.OUTBOUND.getCode());
        addCommodityStockRecordDto.setSecondaryType(CommodityStockRecordSecondTypeEnum.VENDING_MACHINE_OUT.getCode());
        addCommodityStockRecordDto.setOperationUserId(orderListByApplet.getUserId());
        addCommodityStockRecordDto.setOutDisplayRackId(orderListByApplet.getOutDisplayRackId());
        long l_7 = System.currentTimeMillis();
        commodityStockRecordService.add(addCommodityStockRecordDto);
        log.info("售货机是否收到订阅消息，处理慢检查点_7：" + (System.currentTimeMillis() - l_7));

        long l_8 = System.currentTimeMillis();
        List<OrderItemVo> orderItemVoList = orderItemService.findAllByOrderId(order.getId(), orderListByApplet.getOutDisplayRackId());
        log.info("售货机是否收到订阅消息，处理慢检查点_8：" + (System.currentTimeMillis() - l_8));
        for (OrderItemVo orderItemVo : orderItemVoList) {
            if (orderItemVo.getShipmentStatus().equals(ShipmentStatusEnum.SUCCESS.getCode())) {
                CommodityStockRecordItemDto addCommodityStockRecordItemDto = new CommodityStockRecordItemDto();
                addCommodityStockRecordItemDto.setCommodityStockRecordId(addCommodityStockRecordDto.getId());
                addCommodityStockRecordItemDto.setCommodityId(orderItemVo.getCommodityId());
                addCommodityStockRecordItemDto.setQuantity(orderItemVo.getNum());
                addCommodityStockRecordItemDto.setRetailPrice(orderItemVo.getPrice());
                addCommodityStockRecordItemDto.setRetailPriceAfter(orderItemVo.getPrice());
                addCommodityStockRecordItemDto.setRetailPriceBefore(orderItemVo.getPrice());
                addCommodityStockRecordItemDto.setCostPrice(orderItemVo.getCostPrice());
                addCommodityStockRecordItemDto.setCostPriceAfter(orderItemVo.getCostPrice());
                addCommodityStockRecordItemDto.setCostPriceBefore(orderItemVo.getCostPrice());
                addCommodityStockRecordItemDto.setStockAfter(orderItemVo.getStockAfter());
                addCommodityStockRecordItemDto.setStockBefore(orderItemVo.getStockBefore());
                addCommodityStockRecordItemDto.setHierarchy(orderItemVo.getHierarchy());
                addCommodityStockRecordItemDto.setFreightLane(orderItemVo.getFreightLane());
                addCommodityStockRecordItemDto.setRemark("售货机售卖");
                commodityStockRecordItemService.add(addCommodityStockRecordItemDto);
            }
        }
    }


    private void VendingDeviceFaultRecord(DeviceVo deviceVo){
        try {
            if(!StringUtils.isEmpty(deviceVo.getPayLimitTime()) && deviceVo.getPayLimitTime()>0) {
                String memberPayDetailKey = String.format(RedisKey.LIMITATION_SIMULTANEOUS_PAYMENT_VENDING_MACHINE, deviceVo.getId());
                redisCacheTemplate.opsForValue().set(memberPayDetailKey, 2, 30, TimeUnit.DAYS);
                DeviceFaultRecord deviceFaultRecord = DeviceFaultRecord.builder().
                        deviceId(deviceVo.getId()).
                        userId(deviceVo.getUserId()).
                        merchantStoreId(deviceVo.getMerchantStoreId()).
                        fault(1).
                        name("出货失败").
                        build();
                wxSendMessageService.sendFaultMessage(deviceVo.getDeviceNo(), deviceVo.getStoreName(), DateTimeUtils.currTime(), null, deviceFaultRecord.getName(), deviceVo.getUserId(), deviceFaultRecord.getMerchantStoreId());
            }
        }catch (Exception e){
            log.info("插入售货机故障失败");
        }

    }
}
